﻿using EasyAdmin.Application.Contracts.Systems.Menus;
using EasyAdmin.Application.Contracts.Systems.Menus.Dtos;
using EasyAdmin.Domain.Entities.Systems;
using EasyAdmin.Domain.Shared.Enums;
using EasyCore.Application;
using EasyCore.ObjectMapper;
using EasyCore.UnifyResult.Exceptions;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EasyAdmin.Application.Systems
{
    /// <summary>
    /// 菜单管理应用类
    /// </summary>
    public class MenuAppService : AppService, IMenuAppService
    {
        /// <summary>
        /// 菜单仓储
        /// </summary>
        private readonly ISqlSugarRepository<SysMenu> _menuRepo;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="menuRepo"></param>
        public MenuAppService(ISqlSugarRepository<SysMenu> menuRepo)
        {
            this._menuRepo = menuRepo;
        }

        /// <summary>
        /// 创建菜单
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task CreateMenu(CreateUpdateMenuInput input)
        {
            if (await _menuRepo.AnyAsync(m => m.ParentId == input.ParentId && m.Title == input.Title))
            {
                BusinessThrow.Throw("当前目录下存在相同的目录（菜单）名称!");
            }
            var menu = input.MapTo<SysMenu>();
            await _menuRepo.InsertAsync(menu);
        }

        /// <summary>
        /// 更新菜单
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="OperateBusinessException"></exception>
        public async Task UpdateMenu(CreateUpdateMenuInput input)
        {
            if (!input.Id.HasValue) BusinessThrow.Throw("缺少数据唯一标识，导致数据无法进行更新!");
            if (await _menuRepo.AnyAsync(m => m.ParentId == input.ParentId && m.Title == input.Title && m.Id != input.Id.Value))
            {
                BusinessThrow.Throw("当前目录下存在相同的目录（菜单）名称!");
            }
            var menu = await _menuRepo.FirstOrDefaultAsync(m => m.Id == input.Id);
            if (menu == null) BusinessThrow.Throw("该操作为非法操作，请确定后再操作!");
            menu = input.MapTo<CreateUpdateMenuInput, SysMenu>(menu);
            await _menuRepo.UpdateAsync(menu);
        }

        /// <summary>
        /// 创建菜单操作按钮
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task CreateMenuOperation(CreateUpdateMenuOperation input)
        {
            var menu = await _menuRepo.FirstOrDefaultAsync(m => m.Id == input.MenuId);
            if (menu == null) BusinessThrow.Throw("该操作为非法操作，请确定后再操作！");
            if (menu.MenuType == MenuType.Directory) BusinessThrow.Throw("菜单类型为目录，导致添加失败！");
            var _menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            if (await _menuOperationRepo.AnyAsync(m => m.MenuId == input.MenuId
            && (m.OperationCode == input.OperationCode || m.OperationName == input.OperationName))) BusinessThrow.Throw("当前菜单页面下，存在相同的按钮编码或名称！");
            var menuOperation = input.MapTo<SysMenuOperation>();
            await _menuOperationRepo.InsertAsync(menuOperation);
        }

        /// <summary>
        /// 更新菜单操作按钮
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task UpdateMenuOperation(CreateUpdateMenuOperation input)
        {
            var menu = await _menuRepo.FirstOrDefaultAsync(m => m.Id == input.MenuId);
            if (menu == null) BusinessThrow.Throw("该操作为非法操作，请确定后再操作！");
            if (menu.MenuType == MenuType.Directory) BusinessThrow.Throw("菜单类型为目录，导致更新失败！");
            var _menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            if (await _menuOperationRepo.AnyAsync(m => m.MenuId == input.MenuId
            && (m.OperationCode == input.OperationCode || m.OperationName == input.OperationName) && m.Id != input.Id.Value)) BusinessThrow.Throw("当前菜单页面下，存在相同的按钮编码或名称！");
            var menuOperation = await _menuOperationRepo.FirstOrDefaultAsync(m => m.Id == input.Id.Value);
            if (menuOperation == null) BusinessThrow.Throw("该操作为非法操作，请确定后再操作!");
            menuOperation = input.MapTo<CreateUpdateMenuOperation, SysMenuOperation>(menuOperation);
            await _menuOperationRepo.UpdateAsync(menuOperation);
        }

        /// <summary>
        /// 根据菜单ID删除菜单信息
        /// </summary>
        /// <param name="menuId"></param>
        /// <returns></returns>
        /// <exception cref="OperateBusinessException"></exception>
        public async Task DeleteMenu(long menuId)
        {
            var menu = await _menuRepo.FirstOrDefaultAsync(m => m.Id == menuId);
            if (menu == null) BusinessThrow.Throw("数据不存在");
            var roleMenuRepo = _menuRepo.Change<SysRoleMenu>(); //切换到角色菜单仓储
            if (await roleMenuRepo.AnyAsync(m => m.MenuId == menuId)) BusinessThrow.Throw("当前菜单已被角色绑定，暂时无法删除！");
            var menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            if (await menuOperationRepo.AnyAsync(m => m.MenuId == menuId)) BusinessThrow.Throw("当前菜单下已绑定操作，暂时无法删除！");
            await _menuRepo.DeleteAsync(menuId);
        }

        /// <summary>
        /// 根据操作按钮唯一标识删除按钮信息
        /// </summary>
        /// <param name="menuOperationId"></param>
        /// <returns></returns>
        public async Task DeleteMenuOperation(long menuOperationId)
        {
            var menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            var menuOperation = await menuOperationRepo.FirstOrDefaultAsync(m => m.Id == menuOperationId);
            if (menuOperation == null) BusinessThrow.Throw("操作按钮信息不存在！");
            var roleMenuRepo = _menuRepo.Change<SysRoleMenu>();
            if (await roleMenuRepo.AnyAsync(m => m.Operations.Contains(menuOperationId))) BusinessThrow.Throw("当前操作按钮已被角色绑定，暂时无法删除");
            await menuOperationRepo.DeleteAsync(menuOperation);
        }

        /// <summary>
        /// 根据菜单Id获得菜单信息
        /// </summary>
        /// <param name="menuId"></param>
        /// <returns></returns>
        /// <exception cref="OperateBusinessException"></exception>
        public async Task<MenuOutput> GetMenuByMenuId(long menuId)
        {
            var menu = await _menuRepo.FirstOrDefaultAsync(m => m.Id == menuId);
            if (menu == null) return null;
            var menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            var menuOperations = await menuOperationRepo.ToListAsync(m => m.MenuId == menuId);
            var result = menu.MapTo<MenuOutput>();
            if (menuOperations != null && menuOperations.Any()) result.MenuOperations = menuOperations.MapTo<List<MenuOperationOutput>>();
            return result;
        }

        /// <summary>
        /// 获得所有菜单（树型结构）
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<List<MenuOutput>> GetAllMenus()
        {
            var menus = await _menuRepo.ToListAsync();
            var menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            var operations = await menuOperationRepo.ToListAsync();
            return GetChildrenMenu(menus, operations, 0);
        }

        /// <summary>
        /// 获得孩子目录及菜单、以及菜单按钮信息
        /// </summary>
        /// <param name="menus"></param>
        /// <param name="operations"></param>
        /// <param name="parentMenuId"></param>
        /// <returns></returns>
        private List<MenuOutput> GetChildrenMenu(List<SysMenu> menus, List<SysMenuOperation> operations, long parentMenuId)
        {
            var children = menus.Where(m => m.ParentId == parentMenuId).Select(m => m.MapTo<MenuOutput>()).ToList();
            if (children == null || !children.Any()) return null;
            foreach (var item in children)
            {
                item.MenuOperations = operations.Where(m => m.MenuId == item.Id).Select(m => m.MapTo<MenuOperationOutput>()).ToList();
                item.Children = GetChildrenMenu(menus, operations, item.Id);
            }
            return children;
        }

        /// <summary>
        /// 获得菜单的按钮列表
        /// </summary>
        /// <param name="menuId"></param>
        /// <returns></returns>
        public async Task<IEnumerable<MenuOperationOutput>> GetMenuOperations(long menuId){
            var _menuOperationRepo = _menuRepo.Change<SysMenuOperation>();
            var operations = await _menuOperationRepo.ToListAsync(m=>m.MenuId == menuId);
            return operations.MapTo<List<MenuOperationOutput>>(); 
        }

        /// <summary>
        /// 角色绑定菜单
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task RoleBindMenu(RoleBindMenuInput input)
        {
            var roleRepo = _menuRepo.Change<SysRole>(); //角色仓储
            var roleMenuRepo = _menuRepo.Change<SysRoleMenu>();//角色菜单仓储
            var role = await roleRepo.FirstOrDefaultAsync(m=>m.Id == input.RoleId);
            if (role == null) BusinessThrow.Throw("角色不存在，导致绑定失败！");
            var menus = await roleMenuRepo.ToListAsync(m=>m.RoleId == input.RoleId);
            List<SysRoleMenu> insertMenus = new List<SysRoleMenu>();
            List<SysRoleMenu> updateMenus = new List<SysRoleMenu>();
            if (input.Menus != null && input.Menus.Any()) {
                foreach (var item in input.Menus)
                {
                    var temp = menus.FirstOrDefault(m=>m.MenuId == item.MenuId);
                    if (temp != null)
                    {
                        temp.Operations = item.Opertions;
                        updateMenus.Add(temp);
                    }
                    else {
                        insertMenus.Add(new SysRoleMenu() { RoleId = role.Id, MenuId = item.MenuId,Operations = item.Opertions});                    
                    }   
                }
            }
           var deleteMenus = menus.Where(m => !updateMenus.Exists(x => x.MenuId == m.MenuId)).ToList();
            if(insertMenus != null && insertMenus.Any()) await roleMenuRepo.InsertAsync(insertMenus);
            if(updateMenus != null && updateMenus.Any()) await roleMenuRepo.UpdateAsync(updateMenus);
            if(deleteMenus != null && deleteMenus.Any()) await roleMenuRepo.DeleteAsync(deleteMenus);     
        }

        /// <summary>
        /// 获得角色绑定的菜单信息
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<List<RoleMenuOutput>> GetRoleMenus(long roleId)
        {
            var roleMenuRepo = _menuRepo.Change<SysRoleMenu>();
            var roleMenus = await roleMenuRepo.ToListAsync(m=>m.RoleId  == roleId);
            return roleMenus.MapTo<List<RoleMenuOutput>>();        
        } 

        /// <summary>
        /// 获得角色菜单
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<List<UserMenuOutput>> GetUserMenusByUserId(long userId)
        {
            var _userRepo = _menuRepo.Change<SysUser>();
            var userMenus = await _userRepo.AsQueryable().InnerJoin<SysUserRole>((a, b) => a.Id == b.UserId).InnerJoin<SysRoleMenu>((a, b, c) => b.Id == c.RoleId)
                  .InnerJoin<SysMenu>((a, b, c, d) => c.MenuId == d.Id).Where((a, b, c, d) => a.IsDeleted == false && b.UserId == userId).Select((a, b, c, d) => new UserMenuOutput { 
                  MenuId = c.MenuId,
                  ParentMenuId = d.ParentId,
                  Title = d.Title,
                  RoutePath = d.RoutePath,
                  ComponentPath = d.ComponentPath,
                  MenuType = d.MenuType,
                  Icon = d.Icon,
                  IsShow = d.IsShow,
                  IsHome = d.IsHome,
                      IsDisabled = d.IsDisabled,
                      Operations = c.Operations                  
                  } ).Distinct().ToListAsync();
            return GetUserMenuChildren(userMenus, 0);
        }

        /// <summary>
        /// 获得用户菜单的子菜单
        /// </summary>
        /// <param name="menus"></param>
        /// <param name="parentId"></param>
        /// <returns></returns>
        private List<UserMenuOutput> GetUserMenuChildren(List<UserMenuOutput> menus, long parentId)
        {
            var result = menus.Where(m => m.ParentMenuId == parentId).ToList();
            if (result != null && result.Any()) {
                foreach (var item in result)
                {
                    var temp = item.MapTo<UserMenuOutput>();
                    temp.Children = GetUserMenuChildren(menus, item.MenuId);
                    result.Add(temp);
                }
            }
            return result;
        }
    }
}
